use crate::{
    ast::{Expression, Type},
    error::{CompilerError, Result},
    llvm_backend::LLVMBackend,
};
use inkwell::{types::BasicType, values::BasicValueEnum, AddressSpace};

impl<'ctx> LLVMBackend<'ctx> {
    /// Generate slice.push() method call (modifies slice in-place)
    pub(in crate::llvm_backend) fn generate_slice_push_method(
        &mut self,
        var_name: &str,
        arguments: &[Expression],
    ) -> Result<BasicValueEnum<'ctx>> {
        // Get the slice variable pointer from variable_map
        let slice_ptr = self
            .variable_map
            .get(var_name)
            .ok_or_else(|| {
                CompilerError::internal(format!("Undefined slice variable: {}", var_name))
            })?
            .into_pointer_value();

        // Create arguments list: [current_slice, new_elements...]
        let mut append_args = vec![Expression::Variable(var_name.to_string())];
        append_args.extend_from_slice(arguments);

        // Call the append logic
        let new_slice = self.generate_append_call(&append_args)?;

        // Store the new slice back to the variable
        self.builder.build_store(slice_ptr, new_slice)?;

        // Return the new slice (so it can be used in expressions if needed)
        Ok(new_slice)
    }

    /// Generate slice.pop() method call (removes and returns last element)
    pub(in crate::llvm_backend) fn generate_slice_pop_method(
        &mut self,
        var_name: &str,
    ) -> Result<BasicValueEnum<'ctx>> {
        // Get the slice variable pointer from variable_map
        let slice_ptr = self
            .variable_map
            .get(var_name)
            .ok_or_else(|| {
                CompilerError::internal(format!("Undefined slice variable: {}", var_name))
            })?
            .into_pointer_value();

        // Get the slice type
        let vector_type = self.variable_type_map.get(var_name).ok_or_else(|| {
            CompilerError::internal(format!("Undefined slice type for variable: {}", var_name))
        })?;

        let element_type = if let Type::Vec(slice_t) = vector_type {
            *slice_t.element_type.clone()
        } else {
            return Err(CompilerError::internal("pop() requires a slice variable"));
        };

        // Get the element LLVM type
        let element_llvm_type = self.nrc_type_to_llvm_type(&Some(element_type.clone()))?;

        // Create slice struct type
        let ptr_type = element_llvm_type.ptr_type(AddressSpace::default());
        let slice_struct_type = self.context.struct_type(
            &[
                ptr_type.into(),
                self.context.i64_type().into(),
                self.context.i64_type().into(),
            ],
            false,
        );

        // Load current slice
        let current_slice =
            self.builder
                .build_load(slice_struct_type, slice_ptr, "current_slice")?;

        if !current_slice.is_struct_value() {
            return Err(CompilerError::internal("Slice value is not a struct"));
        }

        let slice_struct = current_slice.into_struct_value();

        // Extract fields from the slice struct
        let ptr = self
            .builder
            .build_extract_value(slice_struct, 0, "ptr")?
            .into_pointer_value();
        let len = self
            .builder
            .build_extract_value(slice_struct, 1, "len")?
            .into_int_value();
        let cap = self
            .builder
            .build_extract_value(slice_struct, 2, "cap")?
            .into_int_value();

        // Check if slice is empty (len == 0)
        let zero = self.context.i64_type().const_int(0, false);
        let is_empty =
            self.builder
                .build_int_compare(inkwell::IntPredicate::EQ, len, zero, "is_empty")?;

        // Create basic blocks for control flow
        let parent_fn = self
            .builder
            .get_insert_block()
            .and_then(|b| b.get_parent())
            .ok_or_else(|| CompilerError::internal("No parent function for pop"))?;

        let empty_block = self.context.append_basic_block(parent_fn, "pop_empty");
        let pop_block = self.context.append_basic_block(parent_fn, "pop_element");
        let merge_block = self.context.append_basic_block(parent_fn, "pop_merge");

        // Branch based on whether slice is empty
        let _ = self
            .builder
            .build_conditional_branch(is_empty, empty_block, pop_block);

        // Empty case: return zero value of element type
        self.builder.position_at_end(empty_block);
        let zero_element = self.generate_zero_value(&element_type)?;
        let _ = self.builder.build_unconditional_branch(merge_block);

        // Pop case: get last element and decrement length
        self.builder.position_at_end(pop_block);

        // Calculate index of last element (len - 1)
        let last_idx = self.builder.build_int_sub(
            len,
            self.context.i64_type().const_int(1, false),
            "last_idx",
        )?;

        // Get pointer to last element
        let last_element_ptr = unsafe {
            self.builder
                .build_gep(element_llvm_type, ptr, &[last_idx], "last_element_ptr")?
        };

        // Load the last element
        let popped_element =
            self.builder
                .build_load(element_llvm_type, last_element_ptr, "popped_element")?;

        // Create new slice with decremented length
        let new_len = self.builder.build_int_sub(
            len,
            self.context.i64_type().const_int(1, false),
            "new_len",
        )?;

        // Create new slice struct
        let ptr_type = element_llvm_type.ptr_type(AddressSpace::default());
        let slice_struct_type = self.context.struct_type(
            &[
                ptr_type.into(),
                self.context.i64_type().into(),
                self.context.i64_type().into(),
            ],
            false,
        );

        let new_slice_alloca = self.builder.build_alloca(slice_struct_type, "new_slice")?;

        // Store ptr field (same pointer)
        let ptr_field_ptr = unsafe {
            self.builder.build_gep(
                slice_struct_type,
                new_slice_alloca,
                &[
                    self.context.i32_type().const_int(0, false),
                    self.context.i32_type().const_int(0, false),
                ],
                "ptr_field",
            )?
        };
        self.builder.build_store(ptr_field_ptr, ptr)?;

        // Store new len field
        let len_field_ptr = unsafe {
            self.builder.build_gep(
                slice_struct_type,
                new_slice_alloca,
                &[
                    self.context.i32_type().const_int(0, false),
                    self.context.i32_type().const_int(1, false),
                ],
                "len_field",
            )?
        };
        self.builder.build_store(len_field_ptr, new_len)?;

        // Store cap field (same capacity)
        let cap_field_ptr = unsafe {
            self.builder.build_gep(
                slice_struct_type,
                new_slice_alloca,
                &[
                    self.context.i32_type().const_int(0, false),
                    self.context.i32_type().const_int(2, false),
                ],
                "cap_field",
            )?
        };
        self.builder.build_store(cap_field_ptr, cap)?;

        // Load the new slice
        let new_slice =
            self.builder
                .build_load(slice_struct_type, new_slice_alloca, "new_slice")?;

        // Store the new slice back to the variable
        self.builder.build_store(slice_ptr, new_slice)?;

        let _ = self.builder.build_unconditional_branch(merge_block);

        // Merge block: create PHI node for the result
        self.builder.position_at_end(merge_block);

        // Create PHI node for the popped element
        let phi = self
            .builder
            .build_phi(element_llvm_type, "popped_element_phi")?;
        phi.add_incoming(&[(&zero_element, empty_block), (&popped_element, pop_block)]);

        Ok(phi.as_basic_value())
    }
}
